<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>简易网盘</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <!-- 轻量二维码库 -->
  <script src="https://cdn.jsdelivr.net/npm/qrcodejs2@0.0.2/qrcode.min.js"></script>
  <style>
    :root { --bg:#0f172a; --card:#111827; --muted:#94a3b8; --text:#e5e7eb; --accent:#22c55e; --danger:#ef4444; --border:#1f2937; }
    *{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--text);font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,"Apple Color Emoji","Segoe UI Emoji"}
    .container{max-width:1080px;margin:32px auto;padding:0 16px}
    .card{background:var(--card);border:1px solid var(--border);border-radius:16px;box-shadow:0 10px 30px rgba(0,0,0,.2)}
    .toolbar{display:flex;gap:12px;flex-wrap:wrap;align-items:center;padding:16px;border-bottom:1px solid var(--border)}
    .btn{appearance:none;border:1px solid var(--border);background:#1f2937;color:var(--text);padding:10px 14px;border-radius:12px;font-weight:600;cursor:pointer}
    .btn:hover{filter:brightness(1.15)} .btn.acc{background:var(--accent);border-color:transparent;color:#06220f}
    .btn.dan{background:var(--danger);border-color:transparent}
    .btn.ghost{background:transparent}
    .file-input{display:none}
    .pathbar{padding:14px 16px;display:flex;justify-content:space-between;align-items:center}
    .crumbs{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
    .crumbs a{color:#FFF;text-decoration:none} .crumbs span{color:var(--muted)}
    .list{padding:8px 0}
    table{width:100%;border-collapse:collapse}
    th,td{padding:12px 14px;border-bottom:1px solid var(--border)}
    th{text-align:left;color:var(--muted);font-weight:600}
    tr:hover{background:#0b1222}
    .name{display:flex;align-items:center;gap:10px}
    .thumb{width:38px;height:38px;border-radius:8px;background:#0b1020;object-fit:cover;border:1px solid var(--border)}
    .checkbox{width:18px;height:18px}
    .search{margin-left:auto;display:flex;gap:8px}
    .pill{background:#0b1222;padding:10px 12px;border:1px solid var(--border);border-radius:12px;color:var(--text)}
    .badge{font-size:12px;color:#93c5fd;background:#0b1222;border:1px solid var(--border);padding:2px 8px;border-radius:999px}
    .right{display:flex;gap:8px;align-items:center}
    dialog{border:none;border-radius:16px;padding:0;overflow:hidden}
    .modal{min-width: min(860px, 94vw); background:var(--card); color:var(--text); border:1px solid var(--border)}
    .modal .head{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--border)}
    .modal .body{padding:16px}
    .progress{height:8px;background:#0b1222;border:1px solid var(--border);border-radius:999px;overflow:hidden}
    .bar{height:100%;background:var(--accent);width:0%}
    .muted{color:var(--muted)}
    .actions .btn{padding:6px 10px;font-size:13px;text-decoration: none; }
    .empty{padding:24px;text-align:center;color:var(--muted)}
    .name a{    color: #6171ff;}
    /* ===== 手机端优化 (屏幕宽度 <= 768px) ===== */
@media (max-width: 768px) {
  .container { margin:16px auto; padding:0 8px; }
  .toolbar { flex-direction: column; align-items: stretch; }
  .search { width: 100%; margin-top: 8px; }
  .pill { flex:1; width:100%; }

  /* 面包屑缩小字体 */
  .crumbs { font-size:14px; gap:4px; }

  /* 表格改为卡片式 */
  table, thead, tbody, th, td, tr { display: block; }
  thead { display: none; }
  tr { background: var(--card); border:1px solid var(--border); border-radius:12px; padding:10px; margin-bottom:12px; }
  td { border:none; padding:6px 0; }
  td:first-child { display:none; } /* 隐藏批量选择框，可考虑改为右上角按钮 */
  .name { font-size:15px; font-weight:600; margin-bottom:6px; }
  .thumb { width:32px; height:32px; }

  .actions { display:flex; flex-wrap: wrap; gap:6px; margin-top:6px; }
  .actions .btn { flex:1; min-width: calc(50% - 6px); font-size:14px; padding:8px;text-decoration: none;text-align: center;  }

  /* 文件详情弹窗全屏 */
  #fileDetailModal .modal-content {
    width: 100% !important;
    height: 100% !important;
    border-radius: 0 !important;
    margin:0 !important;
    padding:12px;
    overflow-y:auto;
  }

  #fileDetailModal h2 { font-size:16px; }

  /* 音频播放器条 */
  #audioPlayerBar { width:100%; }
  #audioPlayer { width:100%; }
 
}
 a { text-decoration: none; }
 
 
 /* 拖拽覆盖全屏提示 */
#drag-overlay {
  display: none; /* 默认隐藏 */
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0, 0, 0, 0.25); /* 半透明遮罩 */
  z-index: 9998;
  pointer-events: none;
  text-align: center;
  font-size: 24px;
  color: #fff;
  padding-top: 20%;
  font-weight: bold;
}

  </style>
</head>
<body>
    
    
    <!-- 文件详情弹窗 -->
<div id="fileDetailModal" class="modal" style="display:none;
  position:fixed;top:0;left:0;width:100%;height:100%;
  background:rgba(0,0,0,0.5);z-index:9999;align-items:center;justify-content:center;">

  <div class="modal-content" style="background:#333;border-radius:10px;
    max-width:800px;width:90%;padding:20px;position:relative;margin: auto;">

    <span id="closeDetail" style="position:absolute;top:10px;right:15px;
      font-size:20px;cursor:pointer;">&times;</span>

    <h2 id="detailTitle" style="margin-top:0;font-size:18px;">文件详情</h2>

    <div id="preview" style="margin:10px 0;max-height:400px;overflow:auto;
      border:1px solid #eee;padding:10px;text-align:center;">
      预览区域
    </div>

    <p><strong>文件名：</strong> <span id="detailName"></span></p>
    <p><strong>大小：</strong> <span id="detailSize"></span></p>
    <p><strong>类型：</strong> <span id="detailType"></span></p>
    <p><strong>修改时间：</strong> <span id="detailMtime"></span></p>

    <div style="margin-top:15px;text-align:right;">
      <a id="downloadBtn"  download class="btn btn-primary" 
         style="padding:8px 16px;background:#007bff;color:#fff;
         text-decoration:none;border-radius:6px;">下载</a>
      <div id="qrcode" style="margin-top:10px;text-align:center;"></div>
    </div>

  </div>
</div>

    
    
    
    
<div class="container">
  <div class="card">
    <div class="toolbar">
        

      <button class="btn acc" id="btn-upload">上传文件</button>
      <input type="file" id="file-input" class="file-input" multiple>
      <button class="btn acc" id="btn-folder">📁 上传文件夹</button>
      <input type="file" id="folder-input" class="file-input" webkitdirectory directory multiple>
      <button class="btn " id="btn-newfile">新建文件</button>
      <button class="btn" id="btn-mkdir">新建文件夹</button>
      <button class="btn dan" id="btn-delete-selected">批量删除</button>
      <button class="btn" id="btn-download-selected">批量下载</button>

        <!-- 拖拽上传区域 -->
        <div id="drag-overlay">松开鼠标上传文件</div>

  <div id="drag-upload" style="flex:1; margin-left:12px; padding:10px; border:2px dashed var(--border); border-radius:12px; text-align:center; color:var(--muted); cursor:pointer;">
    拖拽文件到这里或点击上传
    <input type="file" id="drag-file-input" multiple style="display:none">
  </div>
      <div class="search">
        <input id="search" class="pill" placeholder="搜索文件名...">
      </div>
    </div>
    <div class="pathbar">
      <div class="crumbs" id="breadcrumbs"></div>
      <div class="right">
        <span class="badge" id="quota"></span>
      </div>
    </div>
    <div class="list">
      <table>
        <thead>
          <tr>
            <th style="width:36px"><input type="checkbox" id="check-all" class="checkbox"></th>
            <th>名称</th>
            <th style="width:140px">大小</th>
            <th style="width:130px">修改时间</th>
            <th style="width:350px">操作</th>
          </tr>
        </thead>
        <tbody id="tbody"></tbody>
      </table>
      <div class="empty" id="empty" style="display:none">这里还没有文件哦～</div>
    </div>
  </div>
</div>

<dialog id="dlg-upload" class="modal">
  <div class="head"><strong id="upload-title">上传任务</strong><button class="btn ghost" onclick="dlgUpload.close();uploadTotal=0;uploadDone=0;">关闭</button></div>
  <div class="body">
    <div id="upload-list"></div>
  </div>
</dialog>


<!-- 等待提示层 -->
<div id="loading" style="
  display:none;
  position:fixed; top:0; left:0; right:0; bottom:0;
  background:rgba(0,0,0,0.6);
  color:#fff;
  font-size:18px;
  text-align:center;
  padding-top:20%;
  z-index:9999;
">
  正在压缩，请稍候...
</div>


<!-- 移动文件/文件夹弹窗 -->
<!-- 移动文件/文件夹弹窗 -->
<div id="moveModal" class="move-modal" style="display:none;">
  <div class="move-modal-content">
    <h3>选择目标目录</h3>
    <div id="dirTree" class="dir-tree"></div>
    <div id="selectedDir" style="margin:10px 0; font-size:14px; color:#555;">未选择</div>
    <div class="actions">
      <button id="confirmMove">确认移动</button>
      <button id="cancelMove">取消</button>
    </div>
  </div>
</div>


<style>
/* 移动文件弹窗整体 */
.move-modal {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.6); /* 半透明黑色背景 */
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}

/* 弹窗内容 */
.move-modal-content {
  background: #ffffff;   /* 纯白底 */
  color: #333333;        /* 深灰文字 */
  padding: 20px;
  border-radius: 10px;
  width: 360px;
  max-height: 75%;
  overflow-y: auto;
  box-shadow: 0 4px 20px rgba(0,0,0,0.25); /* 阴影更明显 */
  font-family: "Segoe UI", "PingFang SC", sans-serif;
}

/* 标题 */
.move-modal-content h3 {
  margin: 0 0 12px 0;
  font-size: 16px;
  font-weight: 600;
  color: #222; /* 深一点 */
}

/* 已选择提示 */
#selectedDir {
  margin: 12px 0;
  font-size: 14px;
  color: #555;
  padding: 6px 8px;
  background: #f8f8f8;
  border-radius: 6px;
}

/* 目录树 */
ul.tree {
  list-style: none;
  padding-left: 18px;
  margin: 0;
}

ul.tree li {
  margin: 3px 0;
  line-height: 20px;
  font-size: 14px;
  color: #333;
}

ul.tree li .arrow,
ul.tree li .arrow-placeholder {
  display: inline-block;
  width: 16px;
  cursor: pointer;
  color: #666;
  font-size: 12px;
}

ul.tree li .folder {
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  transition: background 0.2s, color 0.2s;
}

ul.tree li .folder:hover {
  background: #f0f4ff; /* 淡蓝色 hover */
  color: #0056b3;
}

ul.tree li .folder.selected {
  background: #007bff;
  color: #ffffff;
  font-weight: 500;
}

/* 底部按钮区 */
#moveModal .actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 15px;
}

#moveModal button {
  padding: 6px 14px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s;
}

#moveModal button#confirmMove {
  background: #007bff;
  color: #fff;
}

#moveModal button#confirmMove:hover {
  background: #0056b3;
}

#moveModal button#cancelMove {
  background: #e0e0e0;
  color: #333;
}

#moveModal button#cancelMove:hover {
  background: #c0c0c0;
}

</style>



<!-- 全局音频播放器 -->
<div id="audioPlayerBar" style="width:100%;
  background:#111827;color:#fff;display:none;align-items:center;justify-content:center;
  padding:8px;z-index:10000;box-shadow:0 -2px 6px rgba(0,0,0,0.3);">
  <audio id="audioPlayer" controls style="width:95%;"></audio>
</div>

<script>
const API = 'api.php';
const DL  = '/storage/';//'download.php';
let currentPath = '';
const dlgUpload = document.getElementById('dlg-upload');
const dlgDetail = document.getElementById('dlg-detail');

$(function(){
  loadList('');
  $('#btn-upload').on('click', ()=> $('#file-input').click());
  $('#btn-folder').on('click', ()=> $('#folder-input').click());
  $('#file-input').on('change', e=> handleFiles(e.target.files));
  $('#folder-input').on('change', e=> handleFiles(e.target.files));
  $('#btn-mkdir').on('click', mkdir);
  $('#check-all').on('change', function(){ $('tbody input[type=checkbox]').prop('checked', this.checked); });
  $('#btn-delete-selected').on('click', batchDelete);
  $('#search').on('input', filterList);
  
  
  const dragArea = $('#drag-upload');
const dragInput = $('#drag-file-input');

dragArea.on('click', ()=> $('#file-input').click());

dragInput.on('change', e => handleFiles(e.target.files));

dragArea.on('dragover', e => { e.preventDefault(); e.stopPropagation(); dragArea.css('background','#1f2937'); });
dragArea.on('dragleave', e => { e.preventDefault(); e.stopPropagation(); dragArea.css('background','transparent'); });
dragArea.on('drop', e => {
  e.preventDefault();
  e.stopPropagation();
  dragArea.css('background','transparent');
  handleFiles(e.originalEvent.dataTransfer.files);
});
});



$('#btn-newfile').on('click', newFile);

function newFile(){
  const name = prompt('请输入新文件名称（例如 example.txt）：');
  if(!name) return;

  // 默认空文件
  $.post(API, {action:'newfile', path: currentPath, name}, res=>{
    if(res.ok) loadList(currentPath);
    else alert(res.msg || '创建失败');
  }, 'json');
}


function breadCrumbs(path){
  const parts = path.split('/').filter(Boolean); let html = '<a href="#" onclick="loadList(\'\')">根目录</a>';
  let p = '';
  for(let i=0;i<parts.length;i++){ p += (i?'/':'') + parts[i]; html += ' <span>/</span> <a href="#" onclick="loadList(\''+p+'\')">'+parts[i]+'</a>'; }
  $('#breadcrumbs').html(html);
}

function humanSize(bytes){ if(bytes===null) return '-'; const u=['B','KB','MB','GB','TB']; let i=0; while(bytes>=1024 && i<u.length-1){bytes/=1024;i++;} return bytes.toFixed( (i?1:0) )+ ' ' +u[i]; }

function filterList(){ const q=$('#search').val().toLowerCase(); $('#tbody tr').each(function(){ const name=$(this).data('name').toLowerCase(); $(this).toggle(name.includes(q)); }); }


function loadList(path){
    $("#audioPlayerBar").hide();
  $.getJSON(API, {action:'list', path:path}, (res)=>{
    if(!res.ok){ alert(res.msg||'加载失败'); return; }
    currentPath = res.path; breadCrumbs(res.path); $('#quota').text(`共 ${res.total} 项，${humanSize(res.bytes)}`);
    const tbody = $('#tbody').empty();
    if(res.items.length===0){ $('#empty').show(); } else { $('#empty').hide(); }
    for(const it of res.items){
     
      const tr = $('<tr>').attr('data-name', it.name).attr('data-path', it.rel);
      tr.append(`<td><input type="checkbox" class="checkbox" data-path="${it.rel}"></td>`);

      const icon = it.type==='dir' ? '📁' : (it.isImage?`<img class="thumb" src="${API}?action=thumb&path=${encodeURIComponent(it.rel)}">`:'📄');
      const nameCell = $('<td>').append(
        $('<div class="name">').append(icon).append(
          $('<a href="#">').text(it.name).on('click', ()=>{ 
            if(it.type==='dir'){ loadList(it.rel); } 
            else { openDetail(it.rel); } 
          })
        )
      );
      tr.append(nameCell);
      tr.append(`<td>${it.type==='dir'?'—':humanSize(it.size)}</td>`);
      tr.append(`<td>${it.mtime}</td>`);

      // 🔧 操作列
      const actions = $('<td class="actions">');
      if(it.type==='file'){
        actions.append($('<button class="btn">详情</button>').on('click',()=>openDetail(it.rel)));
        actions.append($('<button class="btn">重命名</button>').on('click',()=>renameItem(it)));
        actions.append($('<a class="btn" download>下载</a>').attr('href', DL+it.rel));
        actions.append($('<button class="btn dan">删除</button>').on('click',()=>deleteItems([it.rel])));
tr.attr('draggable', true);

    tr.on('dragstart', function(e){
      e.originalEvent.dataTransfer.setData('path', it.rel);
    });
        // 🔊 如果是音频文件，添加播放按钮
        if (it.isAudio) {
          actions.append($('<button class="btn">▶ 播放</button>').on('click',()=>playAudio(it.rel)));
        }
      } else {
        actions.append($('<button class="btn">进入</button>').on('click',()=>loadList(it.rel)));
         actions.append($('<button class="btn">重命名</button>').on('click',()=>renameItem(it)));
  
        actions.append($('<button class="btn dan">删除文件夹</button>').on('click',()=>deleteFolder(it.rel)));
        
      }
      actions.append($('<button class="btn">移动</button>').on('click',()=>{
          openMoveModal(it.rel);
        }));

        if(it.type === 'dir'){
    tr.on('dragover', function(e){ e.preventDefault(); $(this).css('background','#1a2238'); });
    tr.on('dragleave', function(){ $(this).css('background',''); });
    tr.on('drop', function(e){
      e.preventDefault();
      $(this).css('background','');
      const src = e.originalEvent.dataTransfer.getData('path');
      const dest = it.rel;
      if(src && dest){
        $.post(API, {action:'move', src, dest}, res=>{
          if(res.ok) loadList(currentPath);
          else alert(res.msg || '移动失败');
        }, 'json');
      }
    });
    
    
actions.append($('<button class="btn">打包下载</button>').on('click',()=>{
  $('#loading').show();
  $.post(API, {action:'zip', paths: JSON.stringify([it.rel])}, res=>{
    $('#loading').hide();
    if(res.ok){
      window.location = res.url;
    } else {
      alert(res.msg || '打包失败');
    }
  }, 'json').fail(()=>{
    $('#loading').hide();
    alert('压缩请求失败');
  });
}));

  
  
  }
      tr.append(actions); tbody.append(tr);
    }
    filterList();
  });
}

function mkdir(){
  const name = prompt('新建文件夹名称：'); if(!name) return;
  $.post(API, {action:'mkdir', path: currentPath, name}, res=>{ if(res.ok){ loadList(currentPath); } else alert(res.msg||'失败'); }, 'json');
}

function renameItem(it){
  const name = prompt('重命名为：', it.name); if(!name||name===it.name) return;
  $.post(API, {action:'rename', path: it.rel, name}, res=>{ if(res.ok){ loadList(currentPath); } else alert(res.msg||'失败'); }, 'json');
}

function batchDelete(){ const paths = $('tbody input[type=checkbox]:checked').map((_,el)=>$(el).data('path')).get(); if(paths.length===0) return alert('请先选择条目'); deleteItems(paths); }
function deleteItems(paths){ if(!confirm('确定删除选中的文件？')) return; $.ajax({url:API, method:'POST', data:{action:'delete', paths: JSON.stringify(paths)}, dataType:'json', success:(res)=>{ if(res.ok){ loadList(currentPath); } else alert(res.msg||'删除失败'); }}); }
function deleteFolder(path){ if(!confirm('确定删除该文件夹（包含其内所有文件）？')) return; $.post(API, {action:'rmdir', path}, res=>{ if(res.ok){ loadList(currentPath); } else alert(res.msg||'删除失败'); }, 'json'); }


// 移动文件到指定目录——————————————————————————————
let selectedTargetDir = null;

function loadDirTree(container){
  $.getJSON('api.php?action=tree', function(res){
    if(res.ok){
      const html = renderTree(res.tree);
      $(container).html(html);
      bindTreeEvents(container);
    }
  });
}

function renderTree(nodes){
  let html = '<ul class="tree">';
  for(let n of nodes){
    const hasChildren = n.children && n.children.length > 0;
    html += `<li data-path="${n.rel}" class="${hasChildren ? 'has-children' : ''}">
      ${hasChildren ? '<span class="arrow">▶</span>' : '<span class="arrow-placeholder"></span>'}
      <span class="folder">${n.name}</span>`;
    if(hasChildren){
      html += `<div class="children" style="display:none;">${renderTree(n.children)}</div>`;
    }
    html += '</li>';
  }
  html += '</ul>';
  return html;
}

function bindTreeEvents(container){
  // 选择文件夹
  $(container).find('span.folder').off('click').on('click', function(e){
    e.stopPropagation();
    $(container).find('span.folder').removeClass('selected');
    $(this).addClass('selected');
    selectedTargetDir = $(this).parent().data('path');
    $('#selectedDir').text("已选择目录: " + selectedTargetDir);
  });

  // 展开折叠
  $(container).find('span.arrow').off('click').on('click', function(e){
    e.stopPropagation();
    const $li = $(this).closest('li');
    const $children = $li.children('.children');
    if($children.is(':visible')){
      $children.slideUp(150);
      $(this).text('▶');
    } else {
      $children.slideDown(150);
      $(this).text('▼');
    }
  });
}


// 打开移动弹窗
function openMoveModal(srcPath){
  window.moveSrc = srcPath;
  selectedTargetDir = null;
  $('#dirTree').empty();
  $('#selectedDir').text("未选择");
  loadDirTree('#dirTree');
  $('#moveModal').show();  // 这里不需要改，id 还是 moveModal
}

// 确认/取消按钮
$('#confirmMove').on('click', function(){
  if(!selectedTargetDir){
    alert("请先选择一个目标目录");
    return;
  }
  $.post(API, {action:'move', src: window.moveSrc, dest: selectedTargetDir}, r=>{
    if(r.ok){
      $('#moveModal').hide();
      loadList(currentPath);
    } else {
      alert(r.msg || '移动失败');
    }
  },'json');
});

$('#cancelMove').on('click', ()=>$('#moveModal').hide());

// ========== 文件详情与预览 ==========
function openDetail(path){
    $("#audioPlayerBar").hide();
  $.getJSON(API, {action:'info', path}, (res)=>{
    if(!res.ok){ alert(res.msg||'获取详情失败'); return; }
    const d = res.data;
    $("#detailTitle").text("文件详情");
    $("#detailName").text(d.name);
    $("#detailSize").text(formatSize(d.size));
    $("#detailType").text(d.mime);
    $("#detailMtime").text(d.mtime);

    let html = '';
    if (d.isImage) {
      html = `<img src="${API}?action=raw&path=${encodeURIComponent(path)}" style="max-width:100%;border-radius:12px;border:1px solid var(--border)">`;
    } else if (d.isVideo) {
      html = `<video controls style="max-width:100%;max-height:400px;border-radius:12px;border:1px solid var(--border)">
                <source src="${API}?action=raw&path=${encodeURIComponent(path)}">
                你的浏览器不支持视频播放
              </video>`;
    } else if (d.isAudio) {
      html = `<audio controls style="width:100%;">
                <source src="${API}?action=raw&path=${encodeURIComponent(path)}">
                你的浏览器不支持音频播放
              </audio>`;
    } else if (d.isText) {
          html = `<textarea id="text-editor" style="width:100%;height:300px;
           background:#0b1222;border:1px solid var(--border);color:#fff;
           border-radius:12px;padding:10px;resize:vertical;">${$('<div>').text(d.text).html()}</textarea>
          <button class="btn acc" id="save-text" style="margin-top:10px;">保存</button>`;

      //html = `<pre style="white-space:pre-wrap;background:#0b1222;border:1px solid var(--border);padding:12px;border-radius:12px;max-height:60vh;overflow:auto">${$('<div>').text(d.text).html()}</pre>`;
    } else {
      html = `<p class="muted">该类型不支持预览，请直接下载。</p>`;
    }
    $("#preview").html(html);
    const downloadUrl = DL+encodeURIComponent(path);//`${API}?action=raw&path=${encodeURIComponent(path)}`;
    var htmltitle= `
  <strong>文件详情与预览 URL：</strong>
  <div style="display:flex;gap:8px;align-items:center;margin-top:4px;">
    <input type="text" id="file-url" value="${window.location.origin}${downloadUrl}" readonly 
           style="flex:1;padding:6px 8px;border:1px solid var(--border);border-radius:6px;background:#111827;color:#fff;">
    <button class="btn acc" id="copy-url">复制</button>
  </div>
`;
    
$("#detailTitle").html(htmltitle);
    
    $("#downloadBtn").attr("href", downloadUrl);
console.log(downloadUrl)
    $("#qrcode").empty();
    new QRCode(document.getElementById("qrcode"), {
      text: window.location.origin + downloadUrl,
      width: 128,
      height: 128
    });

    $("#fileDetailModal").fadeIn(200);
  });
  


}



$('#btn-download-selected').on('click', function(){
  const paths = $('tbody input[type=checkbox]:checked')
    .map((_,el)=>$(el).data('path')).get();
  if(paths.length === 0) return alert('请先选择文件');

  // 显示等待提示
  $('#loading').show();

  $.post(API, {action:'zip', paths: JSON.stringify(paths)}, res=>{
    $('#loading').hide(); // 请求完成，关闭提示
    if(res.ok){
      window.location = res.url; // 下载
    } else {
      alert(res.msg || '打包失败');
    }
  }, 'json').fail(()=>{
    $('#loading').hide();
    alert('压缩请求失败');
  });
});




$(document).on('click', '#copy-url', function(){
    const url = $('#file-url').val();
    
    if (navigator.clipboard && navigator.clipboard.writeText) {
        // HTTPS/安全环境
        navigator.clipboard.writeText(url).then(()=>{
            alert('已复制到剪贴板');
        }).catch(err=>{
            alert('复制失败，请手动复制');
        });
    } else {
        // fallback
        const $tmp = $('<textarea>').val(url).appendTo('body').select();
        try {
            document.execCommand('copy');
            alert('已复制到剪贴板');
        } catch(e) {
            alert('复制失败，请手动复制');
        }
        $tmp.remove();
    }
});


// 关闭详情
$("#closeDetail").click(function(){
  $("#fileDetailModal").fadeOut(200);
});
$(document).on('click', '#save-text', function(){
  const content = $('#text-editor').val();
  $.post(API, {action:'savefile', path: currentPath+'/'+$("#detailName").text(), content}, res=>{
    if(res.ok) alert('保存成功');
    else alert(res.msg || '保存失败');
  }, 'json');
});

// 文件大小格式化
function formatSize(bytes){
  if (bytes < 1024) return bytes + " B";
  else if (bytes < 1024*1024) return (bytes/1024).toFixed(1) + " KB";
  else if (bytes < 1024*1024*1024) return (bytes/1024/1024).toFixed(1) + " MB";
  else return (bytes/1024/1024/1024).toFixed(1) + " GB";
}

function playAudio(path){
  const url = `${API}?action=raw&path=${encodeURIComponent(path)}`;
  $("#audioPlayer").attr("src", url)[0].play();
  $("#audioPlayerBar").fadeIn(200);
}






const dragOverlay = $('#drag-overlay');

$(document).on('dragover', function(e){
    e.preventDefault();
    e.stopPropagation();
    dragOverlay.show();
});

$(document).on('dragleave', function(e){
    e.preventDefault();
    e.stopPropagation();
    
    // 判断是否离开整个页面
    if(e.target === document || e.target === document.body){
        dragOverlay.hide();
    }
});

$(document).on('drop', function(e){
    e.preventDefault();
    e.stopPropagation();
    dragOverlay.hide();

    const files = e.originalEvent.dataTransfer.files;
    if(files && files.length){
        handleFiles(files);
    }
});


// ========== 上传（支持文件夹 + 分片断点续传） ==========
var uploadDone=0;
var uploadTotal=0;
function updateUploadTitle(){
    
    if(uploadDone==uploadTotal){
        $('#upload-title').text(`上传任务: (${uploadDone}/${uploadTotal})所有文件上传完成✅`);
    }else{
        $('#upload-title').text(`上传任务:正在上传…… (${uploadDone}/${uploadTotal})`);
    }
  
}

function handleFiles(fileList){
    uploadTotal=fileList.length;
  if(fileList.length===0) return; $('#upload-list').empty(); dlgUpload.showModal();
  [...fileList].forEach(file=> queueUpload(file));
}

function makeId(file){ return sha1([file.name, file.size, file.lastModified, file.webkitRelativePath||'', currentPath].join('|')); }

function queueUpload(file){
  const id = makeId(file);
  const row = $(`<div style="margin-bottom:12px">
    <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
      <div class="muted">${file.webkitRelativePath||file.name}</div>
      <div class="muted" id="p-${id}">0%</div>
    </div>
    <div class="progress"><div class="bar" id="b-${id}"></div></div>
  </div>`);
  $('#upload-list').append(row);
  // 先查询已有分片（断点续传）
  $.getJSON(API, {action:'check', uid:id, name:file.name, size:file.size, path: currentPath, rel:file.webkitRelativePath||file.name}, (res)=>{
    const uploaded = new Set(res.uploaded||[]);
    uploadInChunks(file, id, uploaded);
  });
}

async function uploadInChunks(file, uid, uploadedSet){
  const chunkSize = 2 * 1024 * 1024; // 2MB/片
  const total = Math.ceil(file.size / chunkSize);
  for(let i=0;i<total;i++){
    if(uploadedSet.has(i)) { updateProgress(uid, (i+1)/total); continue; }
    const start = i*chunkSize, end = Math.min(start+chunkSize, file.size);
    const blob = file.slice(start, end);
    const form = new FormData();
    form.append('action','upload');
    form.append('uid', uid);
    form.append('index', i);
    form.append('total', total);
    form.append('name', file.name);
    form.append('size', file.size);
    form.append('path', currentPath);
    form.append('rel', file.webkitRelativePath||file.name);
    form.append('file', blob);
    await fetch(API,{method:'POST',body:form}).then(r=>r.json());
    updateProgress(uid, (i+1)/total);
  }
  // 合并
  const res = await fetch(API,{method:'POST', body:new URLSearchParams({action:'merge', uid:uid, name:file.name, size:file.size, path: currentPath, rel:file.webkitRelativePath||file.name})}).then(r=>r.json());
  if(res.ok){ loadList(currentPath); }
}

function updateProgress(uid, ratio){ const pct = Math.floor(ratio*100); $(`#b-${uid}`).css('width', pct+'%'); $(`#p-${uid}`).text(pct+'%');
if(pct==100){
    uploadDone++;
    updateUploadTitle();
}
    
}

// 轻量 sha1（为避免引库，这里实现一个极简 SHA-1）
// 来源：公共域实现，简化版，仅用于生成 uid
function sha1(msg){
  function rotl(n,s){return n<<s|n>>>32-s} function tohex(i){for(var h="",s=28;s>=0;s-=4)h+=(i>>>s&0xf).toString(16);return h}
  var H0=0x67452301,H1=0xEFCDAB89,H2=0x98BADCFE,H3=0x10325476,H4=0xC3D2E1F0;
  var ml=msg.length*8; var with1=unescape(encodeURIComponent(msg)); var l=with1.length; var words=[]; for(var i=0;i<l;i++) words[i>>2]|=with1.charCodeAt(i)<<(24-(i%4)*8);
  words[l>>2]|=0x80<<(24-(l%4)*8); words[((l+8>>6)+1<<4)+15]=ml;
  for(var i=0;i<words.length;i+=16){
    var w=new Array(80); for(var t=0;t<16;t++) w[t]=words[i+t]||0; for(var t=16;t<80;t++) w[t]=rotl(w[t-3]^w[t-8]^w[t-14]^w[t-16],1);
    var a=H0,b=H1,c=H2,d=H3,e=H4;
    for(var t=0;t<80;t++){
      var s=Math.floor(t/20),f=s===0?(b&c)|((~b)&d):s===1?(b^c^d):s===2?((b&c)|(b&d)|(c&d)):(b^c^d),k=[0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6][s];
      var temp=(rotl(a,5)+f+e+k+w[t])>>>0; e=d; d=c; c=rotl(b,30)>>>0; b=a; a=temp;
    }
    H0=(H0+a)>>>0; H1=(H1+b)>>>0; H2=(H2+c)>>>0; H3=(H3+d)>>>0; H4=(H4+e)>>>0;
  }
  return [H0,H1,H2,H3,H4].map(tohex).join("");
}

</script>

</body>
</html>

